package cEvent

import (
	"fmt"
	"reflect"
	"sync"

	"github.com/gin-gonic/gin"

	"gitee.com/csingo/cEvent/interfaces"
)

const (
	event_name_format           = "%s.%s"
	event_handler_function_name = "Subscribe"
)

type EventContainer struct {
	lock      sync.RWMutex
	instances map[string]interfaces.EventInterface
	drivers   map[string]interfaces.DriverInterface
}

func (i *EventContainer) Save(instance any) bool {
	i.lock.Lock()
	defer i.lock.Unlock()

	e := instance.(interfaces.EventInterface)
	app, name := e.EventName()
	driver := e.Driver()

	index := fmt.Sprintf(event_name_format, app, name)
	i.instances[index] = e
	i.drivers[index] = driver

	return true
}

func (i *EventContainer) Get(name string) any {
	i.lock.RLock()
	defer i.lock.RUnlock()

	return i.instances[name]
}

func (i *EventContainer) GetDriver(name string) interfaces.DriverInterface {
	i.lock.RLock()
	defer i.lock.RUnlock()

	return i.drivers[name]
}

func (i *EventContainer) Remove(name string) bool {
	i.lock.Lock()
	defer i.lock.Unlock()

	delete(i.instances, name)

	return true
}

func (i *EventContainer) Is(instance any) bool {
	result := reflect.TypeOf(instance).Implements(reflect.TypeOf((*interfaces.EventInterface)(nil)).Elem())
	v := reflect.ValueOf(instance)
	method := v.MethodByName(event_handler_function_name)
	if !method.IsValid() || method.Type().NumIn() != 2 || method.Type().NumOut() != 1 {
		return false
	}

	t := method.Type().In(0)
	g := reflect.TypeOf((*gin.Context)(nil))
	if t != g {
		return false
	}

	if !method.Type().Out(0).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
		return false
	}

	return result
}

func (i *EventContainer) Range(f func(instance any)) {
	i.lock.RLock()
	defer i.lock.RUnlock()

	for _, item := range i.instances {
		f(item)
	}
}

var container = &EventContainer{
	lock:      sync.RWMutex{},
	instances: make(map[string]interfaces.EventInterface),
	drivers:   make(map[string]interfaces.DriverInterface),
}
